package LDraw.Files; import java.util.ArrayList; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import Command.LDrawColor; import Command.LDrawComment; import Command.LDrawLine; import Command.LDrawPart; import Command.LDrawQuadrilateral; import Command.LDrawTriangle; import Common.Box2; import Common.Box3; import Common.Matrix3; import Common.Matrix4; import LDraw.Support.ILDrawObservable; import LDraw.Support.ILDrawObserver; import LDraw.Support.LDrawDirective; import LDraw.Support.LDrawMetaCommand; import LDraw.Support.PartReport; import LDraw.Support.type.CacheFlagsT; import LDraw.Support.type.MessageT; //============================================================================== // //File: LDrawContainer.m // //Purpose: Abstract subclass for LDrawDirectives which represent a // collection of related directives. // //Created by Allen Smith on 3/31/05. //Copyright (c) 2005. All rights reserved. //============================================================================== /** * @Class LDrawContainer * * @Purpose Abstract subclass for LDrawDirectives which represent a collection * of related directives. * @Represent LDrawContainer.(h, m) of Bricksmith * * @author funface2 * @since 2014-03-13 * */ public abstract class LDrawContainer extends LDrawDirective implements ILDrawObserver, Cloneable { /** * @uml.property name="postsNotifications" */ protected boolean postsNotifications; Lock mutex; /** * @uml.property name="containedObjects" * @uml.associationEnd multiplicity="(0 -1)" * inverse="enclosingDirective:LDraw.Support.LDrawDirective" */ private ArrayList<LDrawDirective> containedObjects; public LDrawContainer() { mutex = new ReentrantLock(true); init(); } // ========== init // ============================================================== // // Purpose: Creates a new container with absolutely nothing in it, but // ready to receive objects. // // ============================================================================== public LDrawContainer init() { super.init(); containedObjects = new ArrayList<LDrawDirective>(); postsNotifications = false; return this; }// end init // ========== allEnclosedElements // =============================================== // // Purpose: Returns all of the terminal (leaf-node) subdirectives contained // within this object and all enclosed containers. Does not return // the enclosed containers themselves. // // ============================================================================== public ArrayList<LDrawDirective> allEnclosedElements() { mutex.lock(); ArrayList<LDrawDirective> subelements = new ArrayList<LDrawDirective>(); for (LDrawDirective currentDirective : containedObjects) { if (LDrawContainer.class.isInstance(currentDirective)) subelements.addAll(((LDrawContainer) currentDirective) .allEnclosedElements()); else subelements.add(currentDirective); } mutex.unlock(); return subelements; }// end allEnclosedElements public long getFlattenWeight(int depth) { long weight = 0; mutex.lock(); for (LDrawDirective currentDirective : containedObjects) { if (currentDirective instanceof LDrawPart) { ((LDrawPart) currentDirective).resolvePart(); if (((LDrawPart) currentDirective).getCacheModel() != null) weight += ((LDrawPart) currentDirective).getCacheModel() .getFlattenWeight(depth * 2); } else if (currentDirective instanceof LDrawStep) weight += ((LDrawStep) currentDirective) .getFlattenWeight(depth * 2); else if (currentDirective instanceof LDrawMetaCommand == false) weight += depth; } mutex.unlock(); return weight; } // // ========== boundingBox3 // ====================================================== // // Purpose: Returns the minimum and maximum points of the box which // perfectly contains this object. // // ============================================================================== // - (Box3) boundingBox3 // { // return [LDrawUtilities boundingBox3ForDirectives:self->containedObjects]; // // }//end boundingBox3 // ========== postsNotifications // ================================================ // ============================================================================== public boolean postsNotifications() { return postsNotifications; } // ========== projectedBoundingBoxWithModelView:projection:view: // ================ // // Purpose: Returns the 2D projection (you should ignore the z) of the // object's bounds. // // ============================================================================== public Box3 projectedBoundingBoxWithModelView(Matrix4 modelView, Matrix4 projection, Box2 viewport) { Box3 bounds = Box3.getInvalidBox(); // Box3 partBounds = Box3.getInvalidBox(); // LDrawDirective currentDirective = null; // int numberOfDirectives = containedObjects.size(); // int counter = 0; // for (counter = 0; counter < numberOfDirectives; counter++) { // currentDirective = containedObjects.get(counter); // } return bounds; }// end projectedBoundingBoxWithModelView:projection:view: // ========== indexOfDirective: // ================================================= // // Purpose: Adds directive into the collection at position index. // // ============================================================================== public int indexOfDirective(LDrawDirective directive) { mutex.lock(); int index = containedObjects.indexOf(directive); mutex.unlock(); return index; }// end indexOfDirective: // ========== subdirectives // ===================================================== // // Purpose: Returns the LDraw directives stored in this collection. // // ============================================================================== public ArrayList<LDrawDirective> subdirectives() { mutex.lock(); ArrayList<LDrawDirective> subDirectives = new ArrayList<LDrawDirective>( containedObjects); mutex.unlock(); return subDirectives; }// end subdirectives public int size() { return containedObjects.size(); } // ========== setPostsNotifications: // ============================================ // // Purpose: Sets whether the container posts // LDrawDirectiveDidChangeNotifications when its contents change. // // Notes: Posting notifications is extremely time-consuming and only // needed for editable containers. Given the huge number of // container changes which occur during parsing, you generally want // this flag off except in parseable directives. // // ============================================================================== /** * @param flag * @uml.property name="postsNotifications" */ public void setPostsNotifications(boolean flag) { postsNotifications = flag; // Apply new setting to children // for (LDrawDirective childDirective : containedObjects) { // } }// end setPostsNotifications: // ========== setVertexesNeedRebuilding // ========================================= // // Purpose: Marks all the vertex optimizations of this container as needing // rebuilding. // // ============================================================================== public void setVertexesNeedRebuilding() { // pass to the superclass; subclasses can override to redirect this // message // to vertexes they manage. if (enclosingDirective() != null) enclosingDirective().setVertexesNeedRebuilding(); } // ========== addDirective: // ===================================================== // // Purpose: Adds directive into the collection at the end of the list. // // ============================================================================== public void addDirective(LDrawDirective directive) { int index = containedObjects.size(); insertDirective(directive, index); }// end addDirective: // ========== collectPartReport: // ================================================ // // Purpose: Collects a report on all the parts in this container, no // matter // how deeply they may be contained. // // ============================================================================== public void collectPartReport(PartReport report) { LDrawDirective currentDirective = null; int counter = 0; // for (counter = 0; counter < containedObjects.size(); counter++) { // currentDirective = containedObjects.get(counter); // todo // Related to GUI event // if(currentDirective // respondsToSelector:@selector(collectPartReport:)]) // [currentDirective collectPartReport:report]; // } }// end collectPartReport: // ========== removeDirective: // ================================================== // // Purpose: Removes the specified LDraw directive stored in this // collection. // // If it isn't in the collection, well, that's that. // // ============================================================================== public void removeDirective(LDrawDirective doomedDirective) { // First, find the object (making sure it's actually there in the // process) int indexOfObject = indexOfDirective(doomedDirective); if (indexOfObject != -1) { // We found it; kill it! removeDirectiveAtIndex(indexOfObject); } }// end removeDirective: // ========== insertDirective:atIndex: // ========================================== // // Purpose: Adds directive into the collection at position index. // // ============================================================================== public void insertDirective(LDrawDirective directive, int index) { // Insert mutex.lock(); if (index >= containedObjects.size()) containedObjects.add(directive); else containedObjects.add(index, directive); mutex.unlock(); directive.setEnclosingDirective(this); // Apply notification policy to new children // todo // related with GUI // if([directive respondsToSelector:@selector(setPostsNotifications:)] // == // YES) // { // if([(LDrawContainer*)directive postsNotifications] != // self->postsNotifications) // { // [(LDrawContainer*)directive // setPostsNotifications:self->postsNotifications]; // } // } // We have to do this FIRST - otherwise, our cache gets rebuilt by // the // notification handlers before the view hierarchy is fully wired // up and things go pretty sideways from there. directive.addObserver(this); sendMessageToObservers(MessageT.MessageObservedChanged); if (postsNotifications == true) { noteNeedsDisplay(); } }// end insertDirective:atIndex: // ========== removeDirectiveAtIndex: // =========================================== // // Purpose: Removes the LDraw directive stored at index in this // collection. // // ============================================================================== public void removeDirectiveAtIndex(int index) { LDrawDirective doomedDirective = containedObjects.get(index); if (doomedDirective.enclosingDirective() == this) doomedDirective.setEnclosingDirective(null); // no parent anymore; // it's an // orphan now. // Do this first...the actual remove may drop the directive's ref // count. // In that // case we'll puke. doomedDirective.removeObserver(this); mutex.lock(); containedObjects.remove(index); // or disowned at least. mutex.unlock(); sendMessageToObservers(MessageT.MessageObservedChanged); if (postsNotifications == true) { noteNeedsDisplay(); } }// end removeDirectiveAtIndex: // ========== setSubdirectiveSelected: // ========================================== // // Purpose: Called by a subdirective when it's been selected. This // allows // container directives to act on child selection. Override in // subclasses. // // ============================================================================== public void setSubdirectiveSelected(boolean subdirective) { // stub } // ========== containsReferenceTo: // ============================================== // // Purpose: Returns if this object (or any of its children) // references a // model with the given name. // // ============================================================================== public boolean containsReferenceTo(String name) { ArrayList<LDrawDirective> subdirectives = subdirectives(); boolean containsReference = false; for (LDrawDirective currentDirective : subdirectives) { containsReference = currentDirective.containsReferenceTo(name); if (containsReference) break; } return containsReference; } // ========== acceptsDroppedDirective: // ========================================== // // Purpose: Returns YES if this container will accept a directive // dropped // on // it. Intended to be overridden by subclasses // // ============================================================================== public boolean acceptsDroppedDirective(LDrawDirective directive) { return true; }// end acceptsDroppedDirective: // ========== // flattenIntoLines:triangles:quadrilaterals:other:currentColor: ===== // // Purpose: Appends the directive into the appropriate container. // // ============================================================================== public void flattenIntoLines(ArrayList<LDrawLine> lines, ArrayList<LDrawTriangle> triangles, ArrayList<LDrawQuadrilateral> quadrilaterals, ArrayList<LDrawDirective> everythingElse, LDrawColor parentColor, Matrix4 transform, Matrix3 normalTransform, boolean recursive) { ArrayList<LDrawDirective> subdirectives = subdirectives(); for (LDrawDirective currentDirective : subdirectives) { currentDirective.flattenIntoLines(lines, triangles, quadrilaterals, everythingElse, parentColor, transform, normalTransform, recursive); } }// end flattenIntoLines:triangles:quadrilaterals:other:currentColor: // ========== // observableSaysGoodbyeCruelWorld==================================== // // Purpose: this is the message receiving method for observers when // their // observable is yanked out from under them. Observation is a weak // reference so the thing you are observing may be nuked. // // In the case of the container, we only observe the directives // we contain and we are supposed to remove all directives before // they die (and containing them is a strong reference). So... // If any dying directives call out to us, it's a programming error. // Just log it out for now? // // ============================================================================== public void observableSaysGoodbyeCruelWorld( ILDrawObservable doomedObservable) { if (containedObjects.indexOf(doomedObservable) != -1) { System.out .println("Observer's observable is dying but we have no idea who it is..."); } } // ========== statusInvalidated // ================================================= // // Purpose: The things we watch call this when one of their states // that // we // might have cached is no longer valid. This tells us to not rely // on that data. // // ============================================================================== public void statusInvalidated(CacheFlagsT flags, ILDrawObservable observable) { invalCache(flags); } // ========== receiveMessage // ==================================================== // // Purpose: The things we observe call this when something one-time // and // eventful happens - we can respond if desired. // // ============================================================================== public void receiveMessage(MessageT msg, ILDrawObservable observable) { switch (msg) { case MessageObservedChanged: invalCache(CacheFlagsT.ContainerInvalid); break; } sendMessageToObservers(msg); } public Object clone() throws CloneNotSupportedException { LDrawContainer a = (LDrawContainer) super.clone(); a.setEnclosingDirective(enclosingDirective); a.containedObjects = new ArrayList<LDrawDirective>(); for (LDrawDirective item : containedObjects){ // if (item instanceof LDrawComment == false){ LDrawDirective newItem =(LDrawDirective) item.clone(); newItem.setEnclosingDirective(a); a.containedObjects.add(newItem); } return a; } public void clear() { containedObjects.clear(); } }